use hashbrown::HashMap;
use honeybee::prelude::*;
use honeybee::Result;

use crate::users::{self, User, UserStream};

struct UserSession {
    session_key: String,
    user_addr: Addr<User>,
    user_stream: Addr<UserStream>,
}

pub struct Usc {
    self_addr: Option<Addr<Self>>,
    users: HashMap<i64, UserSession>,
    usc_server: Addr<TcpConnectersActor>,
}

impl Usc {
    pub async fn new() -> Result<Self> {
        let actor_center = ActorCenter::from_registry().await?;
        let usc_server = actor_center.call(Query::new("usc_server")).await?;

        Ok(Self {
            self_addr: None,
            users: HashMap::default(),
            usc_server,
        })
    }
}

#[async_trait::async_trait]
impl Actor for Usc {
    fn name(&self) -> &str {
        "usc_local"
    }

    async fn started(&mut self, ctx: &mut Context<Self>) -> Result<()> {
        self.self_addr = Some(ctx.address());
        ctx.register_center(self.name()).await?;
        info!("{} started", self.name());
        Ok(())
    }

    async fn stopped(&mut self, ctx: &mut Context<Self>) {
        let _ = ctx.unregister_center().await;
        info!("{} stoped", self.name());
    }
}

#[message]
pub struct LocalUscUserRegister {
    pub uid: i64,
    pub session_key: String,
    pub user_addr: Addr<User>,
    pub user_stream: Addr<UserStream>,
}

#[async_trait::async_trait]
impl Handler<LocalUscUserRegister> for Usc {
    async fn handle(&mut self, _ctx: &mut Context<Self>, msg: LocalUscUserRegister) -> Result<()> {
        let user_session = UserSession {
            session_key: msg.session_key,
            user_addr: msg.user_addr,
            user_stream: msg.user_stream,
        };
        if self.users.insert(msg.uid, user_session).is_some() {
            info!("users register:{} repeated", msg.uid);
        }
        info!("user:{} registed", msg.uid);
        Ok(())
    }
}

#[message]
pub struct LocalUscUserUnregister {
    pub uid: i64,
    pub session_key: String,
}

#[async_trait::async_trait]
impl Handler<LocalUscUserUnregister> for Usc {
    async fn handle(
        &mut self,
        _ctx: &mut Context<Self>,
        msg: LocalUscUserUnregister,
    ) -> Result<()> {
        if let Some(user) = self.users.get(&msg.uid) {
            if user.session_key != msg.session_key {
                return Ok(());
            }
        }
        if self.users.remove(&msg.uid).is_none() {
            error!("users unregister:{} not found user", msg.uid);
        }
        info!("user:{} unregisted", msg.uid);
        Ok(())
    }
}

struct SendUserMsg<T>(i64, T);

impl<T> Message for SendUserMsg<T>
where
    T: 'static + prost::Message + MessageId,
{
    type Result = ();
}

#[async_trait::async_trait]
impl<T> Handler<SendUserMsg<T>> for Usc
where
    T: 'static + prost::Message + MessageId,
{
    async fn handle(&mut self, _ctx: &mut Context<Self>, msg: SendUserMsg<T>) -> Result<()> {
        if let Some(user) = self.users.get(&msg.0) {
            user.user_stream.send(users::SendUserMsg(msg.1))?;
        } else {
            self.usc_server.send_to_user(msg.0, msg.1);
        }
        Ok(())
    }
}

struct SendAppUserMsg<T>(i64, T);

impl<T> Message for SendAppUserMsg<T>
where
    User: Handler<T>,
    T: Message<Result = ()> + HoneybeeMessage,
{
    type Result = ();
}

#[async_trait::async_trait]
impl<T> Handler<SendAppUserMsg<T>> for Usc
where
    User: Handler<T>,
    T: Message<Result = ()> + HoneybeeMessage,
{
    async fn handle(&mut self, _ctx: &mut Context<Self>, msg: SendAppUserMsg<T>) -> Result<()> {
        if let Some(user) = self.users.get(&msg.0) {
            user.user_addr.send(msg.1)?;
        } else {
            self.usc_server.send_to_app_user(msg.0, msg.1);
        }
        Ok(())
    }
}

pub trait LocalSendUser {
    fn send_to_user<T>(&self, uid: i64, msg: T)
    where
        T: 'static + prost::Message + MessageId;

    fn send_to_app_user<T>(&self, uid: i64, msg: T)
    where
        User: Handler<T>,
        T: Message<Result = ()> + HoneybeeMessage;
}

impl LocalSendUser for Addr<Usc> {
    fn send_to_user<T>(&self, uid: i64, msg: T)
    where
        T: 'static + prost::Message + MessageId,
    {
        let _ = self.send(SendUserMsg(uid, msg));
    }

    fn send_to_app_user<T>(&self, uid: i64, msg: T)
    where
        User: Handler<T>,
        T: Message<Result = ()> + HoneybeeMessage,
    {
        let _ = self.send(SendAppUserMsg(uid, msg));
    }
}
